package org.e_gold.service.impl;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.e_gold.dao.UserAccountDAO;
import org.e_gold.factory.AppProperties;
import org.e_gold.factory.Manager;
import org.e_gold.model.UserAccount;
import org.e_gold.service.ConfigService;
import org.e_gold.service.UserAccountService;
import org.e_gold.util.Bet365;
import org.e_gold.util.CalcUtil;
import org.e_gold.util.Constant;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserAccountServiceImpl extends _NavigatorService implements UserAccountService, Constant, Bet365, InitializingBean {

	private static final Logger logger = Logger.getLogger(UserAccountServiceImpl.class);

	@Autowired
	private UserAccountDAO userAccountDAO;

	@Autowired
	private ConfigService configService;

	@Autowired
	private AppProperties appProperties;

	private String USERNAME;
	private String PASSWORD;
	private final String BALANCE_WITHDRAWABLE = "Withdrawable";

	//	@Autowired
	//	public UserAccountServiceImpl(UserAccountDAO userAccountDAO, Environment env, AppProperties appProperties){
	//		this.userAccountDAO = userAccountDAO;
	//		this.env = env;
	//		this.appProperties = appProperties;
	//	}

	@Override
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		this.USERNAME = appProperties.getUserName();
		this.PASSWORD = appProperties.getPassword();
	}

	@Override
	public boolean is_bet_slot_full() {
		int BET_CURRENT_COUNT = configService.get_bet_current_count();
		int BET_TIMES = configService.get_bet_times();

		if(BET_TIMES == 0){
			return false;
		}

		if( BET_TIMES == BET_CURRENT_COUNT){
			return true;
		}

		if( BET_CURRENT_COUNT > BET_TIMES ){
			return true;
		}
		return false;
	}

	@Override
	public void updateAmount_Per_Time(){
		float balance = getRealBalance();

		int BET_CURRENT_COUNT = configService.get_bet_current_count();
		float BET_AMOUNT_PER_TIME = configService.get_bet_amount_per_time();
		int BET_TIMES = configService.get_bet_times();
		int managerSize = Manager.workingContract.size();

		if(managerSize != BET_CURRENT_COUNT){
			BET_CURRENT_COUNT = managerSize;
			configService.set_bet_current_count(BET_CURRENT_COUNT);
		}

		logger.info(" @@ updateAmount_Per_Time 0- balance: " + balance);
		logger.info(" @@ updateAmount_Per_Time 0- BET_CURRENT_COUNT: " + BET_CURRENT_COUNT);
		logger.info(" @@ updateAmount_Per_Time 0- BET_AMOUNT_PER_TIME: " + BET_AMOUNT_PER_TIME);
		if(BET_CURRENT_COUNT == BET_TIMES || balance <= 0f){
			BET_AMOUNT_PER_TIME = 0f;
			logger.info(" @@ -- balance is 0 || bet_slot_full");
		}else{
			if(BET_CURRENT_COUNT == 0){
				BET_AMOUNT_PER_TIME = balance / BET_TIMES;
				logger.info(" @@ -- BET_CURRENT_COUNT = 0");
				logger.info(" @@ -- BET_AMOUNT_PER_TIME = " + BET_AMOUNT_PER_TIME);
			}else{
				if(BET_TIMES - BET_CURRENT_COUNT == 1){ //only 1 left
					if(BET_AMOUNT_PER_TIME == 0f){
						BET_AMOUNT_PER_TIME = balance;
					}else
						//Is there any balance conflicting????
						//if balance is greater than bet_amount 50% = one bet just finished. --> bet = amount per time
						//otherwise, show hand
						if(balance < (BET_AMOUNT_PER_TIME * 1.5)){ 
							BET_AMOUNT_PER_TIME = (balance == BET_AMOUNT_PER_TIME) ? BET_AMOUNT_PER_TIME : balance;
						}
					logger.info(" @@ -- BET_SLOT_LEFT = 1");
					logger.info(" @@ -- BET_AMOUNT_PER_TIME = " + BET_AMOUNT_PER_TIME);
				}else{
					BET_AMOUNT_PER_TIME = (balance / (BET_TIMES - BET_CURRENT_COUNT));
					logger.info(" @@ -- BET_SLOT_LEFT = 0");
					logger.info(" @@ -- BET_AMOUNT_PER_TIME = " + BET_AMOUNT_PER_TIME);
				}
			}
		}
		//		configService.save(Config.builder(appConfig.BET_AMOUNT_PER_TIME, BET_AMOUNT_PER_TIME));
		configService.set_bet_amount_per_time(BET_AMOUNT_PER_TIME);
		UserAccount user = getUser();
		user.setBalance(balance);
		userAccountDAO.update(user);

		logger.info(" @@ updateAmount_Per_Time 1- balance: " + balance);
		logger.info(" @@ updateAmount_Per_Time 1- BET_CURRENT_COUNT: " + BET_CURRENT_COUNT);
		logger.info(" @@ updateAmount_Per_Time 1- BET_AMOUNT_PER_TIME: " + BET_AMOUNT_PER_TIME);
	}

	@Override
	public boolean isLogined() {
		if(appProperties.isTestMode()){
			return true;
		}

		try {
			Document memberAreaPage = redirectNavigator(DEFAULT_URL);
			Element navMenu = memberAreaPage.select("#NavMenu").get(0);
			if(StringUtils.contains(navMenu.html(), BALANCE)){
				return true;
			}
		} catch (Exception e) {
			logger.error(e);
		}
		return false;
	}

	@Override
	public Float getBalance() {

		UserAccount user = getUser();
		if (user != null) {
			return user.getBalance();
		}
		return 0F;
	}

	@Override
	public void setBalance(float balance){
		UserAccount user = getUser();
		user.setBalance(balance);

		userAccountDAO.update(user);
	}

	@Override
	public UserAccount getUser() {

		return userAccountDAO.getCurrentUser();
	}

	@Override
	public Float getRealBalance() {

		if(appProperties.isTestMode()){
			return getBalance();
		}

		Float realBalance = 0f;
		try {
			while (!isLogined()) {
				loginAccount();
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			Document balancePage = redirectNavigator(BALANCE_URL);
			realBalance = getBalanceToBet(balancePage);
		} catch (Exception e) {
			logger.error(e);
		}

		return realBalance;
	}

	private Float getBalanceToBet(Document balancePage) throws Exception{
		try{
			for(Element element : balancePage.select(".MidGrey")){
				String strBalanceType = element.select(".spanPadLeft strong").text();
				if(strBalanceType.equals(BALANCE_WITHDRAWABLE)){
					String balanceStr = element.select(".spanPadRight strong").text();
					return CalcUtil.convertBalance(balanceStr);
				}
			}
		}catch(Exception ex){
			throw ex;
		}

		return 0f;
	}

	@Override
	public void loginAccount() {

		try {
			Document loginPage = redirectNavigator(LOGIN_URL);

			Element loginForm = loginPage.getElementById("aspnetForm");
			// prepare login data
			Map<String, String> loginData = prepareLoginData(loginForm);
			postForm(BASE_URL + loginForm.attr("action"), loginData);
		} catch (Exception e) {
			logger.error(e);
		}
	}

	private Map<String, String> prepareLoginData(Element loginForm) {

		Map<String, String> loginData = new HashMap<String, String>();
		loginData.put("txtUsername", USERNAME);
		loginData.put("txtPassword", PASSWORD);
		for (Element e : loginForm.select("input[type=hidden]")) {
			loginData.put(e.attr("name"), e.attr("value"));
		}
		headers.put(CONTENT_TYPE, CONTENT_TYPE_DATA);
		headers.put(ORIGIN, "");
		headers.put(COOKIE, cookies.getCookie());
		return loginData;
	}

	@Override
	public void login() {

		while (!isLogined()) {
			loginAccount();
			try {
				Thread.sleep(CalcUtil.randomTime(3, 5) * 1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}
